package labs.laba_03;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

public class PrimitiveLine implements Primitive {
	private Dot d1, d2;
	private float A,B,C, k; // =tg(Fi) = -A/B

	public PrimitiveLine(Dot d1, Dot d2) {
		this.d1 = d1;
		this.d2 = d2;
		this.A = d1.getY() - d2.getY();
		this.B = d2.getX() - d1.getX();
		this.C = d1.getX()*d2.getY() - d2.getX()*d1.getY();
		this.k = (A!=0 && B!=0 )?(-1)*this.A/this.B : 0;
	}
	public PrimitiveLine(float A, float B, float C) {
		this.d1 = new Dot();
		this.d2 = new Dot();
		this.A = A;
		this.B = B;
		this.C = C;
		this.k = (A!=0 && B!=0 )?(-1)*this.A/this.B : 0;
	}
	public Dot getD1() {
		return d1;
	}
	public Dot getD2() {
		return d2;
	}
	public float getA() {
		return A;
	}
	public float getAp2() {
		return A*A;
	}
	public float getB() {
		return B;
	}
	public float getBp2() {
		return B*B;
	}
	public float getC() {
		return C;
	}
	@Override
	public float getDistanceToOrigin() {
		float p;
		Dot nearDot = this.getNearestDot();
		if(this.ownDot(nearDot))
			p = nearDot.getDistanceTo(new Dot());
		else
			p = (float)(C/(Math.sqrt(A*A + B*B)));
		return Math.abs(p);
	}
	public float getDistanceTo(Dot dt) {
		float d[];
		d = new float[3];
		Dot od = new Dot();

		 // distance from line to dot
//		d[0] = (float)Math.abs((A*dt.getX()+B*dt.getY()+C)/(Math.sqrt(A*A + B*B)));

		if(A==0){
			od.setX(dt.getX());
			od.setY(d1.getY());
			//d[0] = od.getDistanceTo(dt);
		}else if(B==0){
			od.setX(d1.getX());
			od.setY(dt.getY());
			//d[0] = od.getDistanceTo(dt);
		}else{
			float nC = k==0 ? dt.getY() : dt.getY()-(-1/k)*dt.getX();
			float nB = -1;
			float nA = k==0 ? 0 : (-1/k);
			PrimitiveLine l = new PrimitiveLine(nA, nB, nC);
			od = this.getIntersectionDot(l);
		}

		if(this.ownDot(od))
			return od.getDistanceTo(dt);

		//d[0] = (float)Math.abs((A*dt.getX()+B*dt.getY()+C)/(Math.sqrt(A*A + B*B)));
		d = new float[2];
		d[0] = d1.getDistanceTo(dt);
		// distance from line's dot 2 to dot
		d[1] = d2.getDistanceTo(dt);
		// compare distances, finding min
		Arrays.sort(d);
		return d[0];

	}
	private float getCos(PrimitiveLine l){
		float cos;
		cos = (float)(this.getA()*l.getA()+this.getB()*l.getB())/(float)(Math.sqrt(this.getAp2() + this.getBp2()) * Math.sqrt(l.getAp2() + l.getBp2()));
		return cos;
	}
	public Dot getNearestDot(){
		Dot d = new Dot(0, 0);
		d.setXY((-1)*A*C/(A*A+B*B), (-1)*B*C/(A*A+B*B));
		return d;
	}
	public boolean ownDot(Dot d){
		final float dx = Math.abs(d1.getX()-d2.getX());
		final float dy = Math.abs(d1.getY()-d2.getY());
		if((Math.abs(d1.getX()-d.getX()) <= dx && Math.abs(d2.getX()-d.getX()) <= dx)
		&& (Math.abs(d1.getY()-d.getY()) <= dy && Math.abs(d2.getY()-d.getY()) <= dy)
			) return true;
		return false;
	}
	public float getProjectionX(){
		return Math.abs(d2.getX()-d1.getX());
	}
	public float getProjectionY(){
		return Math.abs(d2.getY()-d1.getY());
	}
	public boolean intersectLine(PrimitiveLine l){
		if((this.A*l.getB()-l.getA()*this.B)==0) return false;
		Dot d = this.getIntersectionDot(l);
		return (this.ownDot(d) && l.ownDot(d));
	}
	public Dot getIntersectionDot(PrimitiveLine l){
		if((this.A*l.getB()-l.getA()*this.B)==0) return new Dot();
		// by Cramer's rule
		Dot d = new Dot();
		d.setX((-1)*((float)((this.C*l.getB()-l.getC()*this.B)/(this.A*l.getB()-l.getA()*this.B))));
		d.setY((-1)*((float)((this.A*l.getC()-l.getA()*this.C)/(this.A*l.getB()-l.getA()*this.B))));
		return d;
	}
	
}
